Imports TreeCreeper01Interfaces
Imports System.Collections.Generic

Friend Class cPlantList
    Implements IPlantList

    Private mTaxonomicDistanceProvider As ITaxonomicDistanceProvider
    Private mLocationsMap As cSpatialPlantMap
    Private mPlantCollection As cPlantCollection

    Public Sub New(ByVal TaxonomicDistanceProvider As ITaxonomicDistanceProvider, Optional ByVal LocationsMap As cSpatialPlantMap = Nothing)

        mTaxonomicDistanceProvider = TaxonomicDistanceProvider
        mPlantCollection = New cPlantCollection

        If LocationsMap Is Nothing Then
            mLocationsMap = New cSpatialPlantMap(1)
        Else
            mLocationsMap = LocationsMap
        End If

    End Sub

    Public Function CreateAnalysis() As IPlantListAnalysis Implements IPlantList.CreateAnalysis

        Dim Analysis As IPlantListAnalysis

        Analysis = New cPlantListAnalysis(Me)
        Return Analysis

    End Function

    Public ReadOnly Property Count() As Integer Implements IPlantList.Count
        Get
            Return mPlantCollection.Count()
        End Get
    End Property

    Public ReadOnly Property PlantByIndexBaseOne(ByVal IndexBaseOne As Integer) As TreeCreeper01Interfaces.IPlant Implements TreeCreeper01Interfaces.IPlantList.PlantByIndexBaseOne
        Get
            Return mPlantCollection.ItemByIndexBaseOne(IndexBaseOne)
        End Get
    End Property

    Public ReadOnly Property PlantByUniqueKey(ByVal UniqueKey As String) As IPlant Implements IPlantList.PlantByUniqueKey
        Get
            Dim Plant As IPlant
            Try
                Plant = mPlantCollection.ItemByKey(UniqueKey)
            Catch
                Plant = Nothing
            End Try
            Return Plant
        End Get
    End Property

    Public Sub AddPlantList(ByVal PlantList As IPlantList) Implements IPlantList.AddPlantList

        Dim PlantIndexBaseOne As Integer
        Dim Plant As IPlant

        For PlantIndexBaseOne = 1 To PlantList.Count()
            Plant = PlantList.PlantByIndexBaseOne(PlantIndexBaseOne)
            Me.AddExistingPlant(Plant)
        Next

    End Sub

    Friend Sub AddNewPlant(ByVal GroupingInteger As Integer, ByVal UniqueKey As String, ByVal ShortId As Integer, ByVal XCoordinate As Double, ByVal YCoordinate As Double, _
       ByVal Species As ITaxonomicSpecies, ByVal CanBeOriginPlantForCircles As Boolean, ByVal CanBeRandomized As Boolean) Implements IPlantList.AddPlant

        Dim Location As cSpatialPlantMapLocation
        Dim Plant As cPlant

        Location = mLocationsMap.AddNewPlantLocation(XCoordinate, YCoordinate)

        ' add to collection
        Plant = New cPlant(GroupingInteger, UniqueKey, ShortId, Location, Species, CanBeOriginPlantForCircles, CanBeRandomized)
        mPlantCollection.Add(Plant)

    End Sub

    Friend Sub AddExistingPlant(ByVal Plant As IPlant) Implements IPlantList.AddClonedPlant

        mPlantCollection.Add(Plant)

    End Sub

    Private Sub CalculateAndCacheAllSpatialDistancesAndShortIds() Implements TreeCreeper01Interfaces.IPlantList.CalculateAndCacheAllSpatialDistancesAndShortIds
        mLocationsMap.CalculateInterdistancesIfNeeded()
    End Sub

    ReadOnly Property MaximumHorizontalOrVerticalSpatialSeparation() As Double Implements IPlantList.MaximumHorizontalOrVerticalSpatialSeparation
        Get
            Return mLocationsMap.MaximumHorizontalOrVerticalSpatialSeparation
        End Get
    End Property

    Public Function TaxonomicDistanceBetweenPlants(ByVal FirstPlant As IPlant, ByVal SecondPlant As IPlant) As Single Implements IPlantList.TaxonomicDistanceBetweenPlants

        Dim TaxonomicDistance As Single

        TaxonomicDistance = mTaxonomicDistanceProvider.TaxonomicDistanceBetweenSpecies(FirstPlant.TaxonomicSpecies, SecondPlant.TaxonomicSpecies)
        Return TaxonomicDistance

    End Function

    Public Function SpatialDistanceBetweenPlants(ByVal FirstPlant As IPlant, ByVal SecondPlant As IPlant) As Double Implements IPlantList.SpatialDistanceBetweenPlants

        Dim FirstLocation As ISpatialPlantMapLocation
        Dim SecondLocation As ISpatialPlantMapLocation

        If FirstPlant Is SecondPlant Then
            Return 0
        Else
            FirstLocation = FirstPlant.LocationCurrent
            SecondLocation = SecondPlant.LocationCurrent
            Return mLocationsMap.GetInterdistanceBetweenTwoLocations(FirstLocation, SecondLocation)
        End If

    End Function

    Friend Sub DoRandomPermutationOfLocations()

        Dim CandidateIndexBaseOne As Integer
        Dim Indexable1 As cIndexablePlant
        Dim SwapCandidates As List(Of cIndexablePlant)

        SwapCandidates = New List(Of cIndexablePlant)

        For CandidateIndexBaseOne = 1 To mPlantCollection.Count
            Indexable1 = New cIndexablePlant(mPlantCollection.ItemByIndexBaseOne(CandidateIndexBaseOne), CandidateIndexBaseOne)
            If Indexable1.Plant.CanBeRandomized Then
                ' only add swap candidates which can truly be randomized!
                SwapCandidates.Add(Indexable1)
            End If
        Next

        'Iterate through each (indexable, ergo randomizable=true) plant and make switch with some random plant
        'I am not 100% sure this is as max. randomness as possible (due to asymmetry??? in back-positioning)
        'Anyhow this should be enough for our purposes?

        Dim PlantUnderlyingIndex1 As Integer
        Dim PlantUnderlyingIndex2 As Integer
        Dim PlantSwappableIndex2 As Integer

        For Each Indexable1 In SwapCandidates

            PlantUnderlyingIndex1 = Indexable1.ListIndex
            PlantSwappableIndex2 = cRandomGenerator.NextRandom(1, SwapCandidates.Count)
            PlantUnderlyingIndex2 = SwapCandidates.Item(PlantSwappableIndex2 - 1).ListIndex

            SwapTwoPlantLocationsByIndex(PlantUnderlyingIndex1, PlantUnderlyingIndex2)

            'Debug.Print(Plant1.UniqueKey.Substring(0, 15) & "/" & PlantIndex1 & " swapped to " & Plant2.UniqueKey.Substring(0, 15) & "/" & PlantIndex2)
            'Debug.Print(PlantIndex1.ToString & ", " & Plant1.XCoordinate.ToString & ", " & Plant1.YCoordinate.ToString)

        Next

    End Sub


    ' Note that the original XY coordinates are not swapped here, so each plant still has XY reference to its original coordinates
    Friend Sub SwapTwoPlantLocationsByIndex(ByVal PlantIndex1 As Integer, ByVal PlantIndex2 As Integer)

        Dim Plant1 As IPlant
        Dim Plant2 As IPlant

        Dim TempLocation As ISpatialPlantMapLocation
        Plant1 = mPlantCollection.ItemByIndexBaseOne(PlantIndex1) ' must point to the new list!
        Plant2 = mPlantCollection.ItemByIndexBaseOne(PlantIndex2) ' must point to the new list!

        TempLocation = Plant1.LocationCurrent
        Plant1.LocationCurrent = Plant2.LocationCurrent
        Plant2.LocationCurrent = TempLocation

        '2008-04-25 DNH. Put this in so that the lists would remain in the same order relative to their locations. This changes the identity at a given list index while maintaining location.
        mPlantCollection.SwapPlantIndices(PlantIndex1, PlantIndex2)

    End Sub

    Friend Function Clone() As cPlantList

        Dim NewPlantList As cPlantList
        Dim PlantIndex As Integer
        Dim OldPlant As cPlant ' clonable
        Dim NewPlant As IPlant

        NewPlantList = New cPlantList(mTaxonomicDistanceProvider, mLocationsMap)

        For PlantIndex = 1 To Me.Count
            OldPlant = CType(Me.PlantByIndexBaseOne(PlantIndex), cPlant)  ' clonable
            NewPlant = OldPlant.Clone '2007-06-27 DNH. Hey Vesa! Don't forget to do the actual cloning. 
            'Otherwise you're just reorganising lists of identical references.
            NewPlantList.AddExistingPlant(NewPlant)
        Next

        Return NewPlantList

    End Function

    Friend Function CreateEmptyCopy() As cPlantList

        Dim NewPlantList As cPlantList

        NewPlantList = New cPlantList(mTaxonomicDistanceProvider, mLocationsMap)
        Return NewPlantList

    End Function


End Class
